package com.jumper.property.comparer.core.diff.getter;

import com.jumper.property.comparer.core.bean.PropertySetting;
import com.jumper.property.comparer.core.diff.Element;
import com.jumper.property.comparer.core.util.ValueUtil;
import com.jumper.property.comparer.core.diff.value.ArrayDiff;
import com.jumper.property.comparer.core.diff.value.DiffEditRow;
import com.jumper.property.comparer.core.diff.value.DiffRow;
import com.jumper.property.comparer.core.diff.value.PrimitiveWrapper;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 普通属性类型数据对比差异
 */
public class ArrayDiffGetter extends AbstractDiffGetter<PropertySetting> {

    @Override
    public void visit(Element<PropertySetting> element) {
        Collection<?> beforeList = convertToCollection(element.getBeforeValue(), element.getSetting());
        Collection<?> afterList = convertToCollection(element.getAfterValue(), element.getSetting());
        List<DiffRow> beforeRows = convertToDiffRow(beforeList, element.getSetting());
        List<DiffRow> afterRows = convertToDiffRow(afterList, element.getSetting());
        // 新增的行
        List<DiffEditRow> addRows = filterAddRows(beforeRows, afterRows);
        // 删除的行
        List<DiffEditRow> removeRows = filterRemoveRows(beforeRows, afterRows);
        // 更新的行
        List<DiffEditRow> updateRows = filterEditRows(beforeRows, afterRows);
        ArrayDiff diff = new ArrayDiff(element.getSetting(), beforeList, afterList, addRows, removeRows, updateRows);
        addDiffs(diff.getDiffValues());
    }

    /**
     * 过滤出编辑的行
     */
    private List<DiffEditRow> filterEditRows(List<DiffRow> beforeRows, List<DiffRow> afterRows) {
        List<String> beforeKeys = filterRowKeys(beforeRows);
        List<String> afterKeys = filterRowKeys(afterRows);
        beforeKeys.removeIf(key -> !afterKeys.contains(key));
        return beforeKeys.stream().map(key -> {
            DiffRow beforeData = beforeRows.stream().filter(e -> Objects.equals(key, e.getRowKey())).findFirst().orElse(null);
            DiffRow afterData = afterRows.stream().filter(e -> Objects.equals(key, e.getRowKey())).findFirst().orElse(null);
            DiffRow rowData = (beforeData == null ? afterData : beforeData);
            String displayName = key;
            if (rowData != null) {
                displayName = rowData.getDisplayName();
            }
            return new DiffEditRow(key, displayName, beforeData == null ? null : beforeData.getData(), afterData == null ? null : afterData.getData(), rowData.getSetting());
        }).collect(Collectors.toList());
    }

    /**
     * 过滤出行标识
     */
    private List<String> filterRowKeys(List<DiffRow> beforeRows) {
        return beforeRows.stream().map(DiffRow::getRowKey).collect(Collectors.toList());
    }


    /**
     * 过滤出移除的行
     */
    private List<DiffEditRow> filterRemoveRows(List<DiffRow> beforeRows, List<DiffRow> afterRows) {
        List<String> afterKeys = filterRowKeys(afterRows);
        List<DiffRow> removeRows = filterConditionRows(beforeRows, afterKeys);
        return removeRows.stream().map(this::buildRemoveRow).collect(Collectors.toList());
    }

    /**
     * 创建删除行标识
     */
    private DiffEditRow buildRemoveRow(DiffRow row) {
        return new DiffEditRow(row.getRowKey(), row.getDisplayName(), row.getData(), null, row.getSetting());
    }

    /**
     * 过滤出新增的行
     */
    private List<DiffEditRow> filterAddRows(List<DiffRow> beforeRows, List<DiffRow> afterRows) {
        List<String> beforeKeys = filterRowKeys(beforeRows);
        List<DiffRow> addRows = filterConditionRows(afterRows, beforeKeys);
        return addRows.stream().map(this::buildAddRow).collect(Collectors.toList());
    }

    /**
     * 构建增加行
     */
    private DiffEditRow buildAddRow(DiffRow row) {
        return new DiffEditRow(row.getRowKey(), row.getDisplayName(), null, row.getData(), row.getSetting());
    }

    /**
     * 过路条件行
     */
    private List<DiffRow> filterConditionRows(List<DiffRow> afterRows, List<String> beforeKeys) {
        return afterRows.stream().filter(row -> !beforeKeys.contains(row.getRowKey())).collect(Collectors.toList());
    }

    /**
     * 对象转换
     */
    private List<DiffRow> convertToDiffRow(Collection<?> beforeList, PropertySetting setting) {
        Map<String, Field> fieldMap = getFieldMap(setting);
        return beforeList.stream().map(row -> {
            String key = getUnionKey(setting.getKeyProperties(), fieldMap, row);
            String displayName = key;
            if (!setting.getDisplayProperties().isEmpty()) {
                displayName = getUnionKey(setting.getDisplayProperties(), fieldMap, row);
            }
            return new DiffRow(key, displayName, row, setting);
        }).collect(Collectors.toList());
    }

    /**
     * 获取对象唯一标识
     */
    private String getUnionKey(List<String> keys, Map<String, Field> fieldMap, Object row) {
        return keys.stream().map(fieldMap::get)
                .map(field -> ValueUtil.getStrValue(row, field))
                .collect(Collectors.joining("-"));
    }

    /**
     * 获取字段映射
     */
    private Map<String, Field> getFieldMap(PropertySetting setting) {
        return setting.getObjectSetting().getPropertySettings().stream()
                .map(PropertySetting::getField)
                .collect(Collectors.toMap(Field::getName, Function.identity()));
    }

    /**
     * 属性转集合
     */
    private Collection<?> convertToCollection(Object value, PropertySetting setting) {
        ArrayList<Object> result = new ArrayList<>();
        if (value == null) {
            return result;
        }
        if (value instanceof Collection) {
            Collection<?> collection = (Collection<?>) value;
            if (setting.getObjectSetting() != null && PrimitiveWrapper.class.isAssignableFrom(setting.getObjectSetting().getCls())) {
                collection = collection.stream().map(PrimitiveWrapper::instance).collect(Collectors.toList());
            }
            return collection;
        }
        if (setting.getField().getType().isArray()) {
            Object[] array = (Object[]) value;
            result.addAll(Arrays.asList(array));
        }
        return result;
    }
}
